<?php

/**
 * Current Http component of request
 */
class Request
{

    /**
     * main request that instance current process
     * @var Request
     */
    static protected $_initiator = null;

    /**
     * get main request
     * @return Request
     */
    static public function initiator()
    {
        if (null == self::$_initiator)
        {
            if (php_sapi_name() == 'cli')
            {
                self::$_initiator = new Request_Cli;
            }
            else
            {
                self::$_initiator = new Request_Http;
            }
        }
        return self::$_initiator;
    }

    /**
     * Retrieve a member of the $_COOKIE superglobal
     *
     * If no $key is passed, returns the entire $_COOKIE array.
     *
     * @todo How to retrieve from nested arrays
     * @param string $key
     * @param mixed $default Default value to use if key not found
     * @return mixed Returns null if key does not exist
     */
    static public function getCookie($key = null, $default = null)
    {
        return null === $key ? $_COOKIE : (isset($_COOKIE[$key]) ? $_COOKIE[$key] : $default);
    }

    /**

     * @param string $key
     * @param string $default
     * @return string
     */
    static public function getServer($key = null, $default = null)
    {
        return null === $key ? $_SERVER : (isset($_SERVER[$key])) ? $_SERVER[$key] : $default;
    }

    /**
     * @param string $key
     * @param string $default
     * @return string
     */
    static public function getEnv($key = null, $default = null)
    {
        return null === $key ? $_ENV : (isset($_ENV[$key])) ? $_ENV[$key] : $default;
    }

    /**
     * @param string $key
     * @return string
     */
    static public function getHeader($key)
    {

        // Try to get it from the $_SERVER array first
        $temp = 'HTTP_' . strtoupper(str_replace('-', '_', $key));

        if (isset($_SERVER[$temp]))
        {
            return $_SERVER[$temp];
        }

        if (function_exists('apache_request_headers'))
        {
            $headers = apache_request_headers();
            if (isset($headers[$key]))
            {
                return $headers[$key];
            }
            $header = strtolower($key);
            foreach ($headers as $k => $v)
            {
                if (strtolower($k) == $key)
                {
                    return $value;
                }
            }
        }

        return false;
    }

    /**
     * @return string
     */
    static public function getScheme()
    {
        return self::getServer('HTTPS') == 'on' ? 'https' : 'http';
    }

    /**
     * Get the HTTP host.
     *
     * "Host" ":" host [ ":" port ] ; Section 3.2.2
     * Note the HTTP Host header is not the same as the URI host.
     * It includes the port while the URI host doesn't.
     *
     * @return string
     */
    static public function getHttpHost()
    {
        $host = self::getServer('HTTP_HOST');

        if (!empty($host))
        {
            return $host;
        }

        $scheme = self::getScheme();
        $name = self::getServer('SERVER_NAME');
        $port = self::getServer('SERVER_PORT');

        if (null === $name)
        {
            return '';
        }
        elseif (($scheme == 'http' && $port == 80) || ($scheme == 'https' && $port == 443))
        {
            return $name;
        }
        else
        {
            return $name . ':' . $port;
        }
    }

    /**
     * @return string
     */
    static public function getClientIp()
    {
        if (self::getServer('HTTP_CLIENT_IP'))
        {
            self::getServer('HTTP_CLIENT_IP');
        }

        if (self::getServer('HTTP_X_FORWARDED_FOR'))
        {
            return self::getServer('HTTP_X_FORWARDED_FOR');
        }

        return self::getServer('REMOTE_ADDR', '127.0.0.1');
    }

    /**
     *
     * @return boolean
     */
    static public function isFlash()
    {
        return strpos(self::getHeader('USER_AGENT'), ' flash') !== false;
    }

    /**
     * Is the request a Javascript XMLHttpRequest?
     *
     * Should work with Prototype/Script.aculo.us, possibly others.
     *
     * @return boolean
     */
    static public function isAjax()
    {
        return (self::getHeader('X_REQUESTED_WITH') == 'XMLHttpRequest');
    }

    /**
     * target format: path/to/controller/action
     * @var string
     */
    protected $_controller = null;

    /**
     * @var array
     */
    protected $_params = array();

    /**
     * main object that touch by this request, it's helful.
     */
    protected $_subject = null;

    /**
     * response body
     */
    public $response = '';

    /**
     * constructor
     * @return void
     */
    public function __construct($controller = null, $params = null)
    {
        $this -> _controller = $controller;
        $params && $this -> setParams($params);
    }

    /**
     * magic get method
     * please do not get this method to fetch from
     * 'subject','response','target','method','params', it's drangourous
     * @param string $name
     * @return mixed
     */
    public function __get($key)
    {
        return isset($this -> _params[$key]) ? $this -> _params[$key] : null;
    }

    /**
     * magic setter method
     * @param   string  $key
     * @param   mixed   $value
     * @return void
     */
    public function __set($key, $value)
    {
        $this -> _params[$key] = $value;
    }

    /**
     * @return string
     */
    public function __toString()
    {
        return $this -> response;
    }

    /**
     * set target path
     * @param string $controller
     * @return Request
     */
    public function setController($controller)
    {
        $this -> _controller = $controller;
        return $this;
    }

    /**
     * get current target
     * if target is null, disptacher will be called to mersure the target
     *
     * @return string
     */
    public function getController()
    {
        if (null === $this -> _controller)
        {
            Router::dispatch($this);
        }
        return $this -> _controller;
    }

    /**
     * set subject of current request
     * @param any $subject
     * @return Request
     */
    public function setSubject($subject)
    {
        $this -> _subject = $subject;
        return $this;
    }

    /**
     * return subject data
     * @return object/array/null
     */
    public function getSubject()
    {
        return $this -> _subject;
    }

    /**
     * set parametter
     * @param array $array
     * @return Request
     */
    public function setParams($array)
    {
        foreach ($array as $key => $value)
        {
            $this -> _params[$key] = $value;
        }
        return $this;
    }

    /**
     * @return array
     */
    public function getParams()
    {
        return $this -> _params;
    }

    /**
     * set param
     * @param string $key
     * @param mixed  $value
     * @return Request
     */
    public function setParam($key, $value)
    {
        $this -> _params[$key] = $value;
        return $this;
    }

    /**
     * @param string $key
     * @param mixed $default_value
     * @return Request
     */
    public function getParam($key, $default_value = null)
    {
        return isset($this -> _params[$key]) ? $this -> _params[$key] : $default_value;
    }

    /**
     * get data
     * @return array
     */
    public function getGet()
    {
        return $this -> _params;
    }

    /**
     * post data
     * @return array
     */
    public function getPost()
    {
        return array();
    }

    /**
     * get method of current request
     * @return string
     */
    public function method()
    {
        return 'GET';
    }

    /**
     * is get method
     * @return TRUE/FALSE
     */
    public function isGet()
    {
        return $this -> method() == 'GET';
    }

    /**
     * return post
     * @return TRUE/FALSE
     */
    public function isPost()
    {
        return $this -> method() == 'POST';
    }

    /**
     * execute by handler
     */
    public function execute()
    {
        $controller = $this -> getController();
		
		
		Router::sanitizeControllerClass($controller, $class);

        if ($class)
        {
			
            // explode controller name
            $object = new $class;

            // execute request
            $object -> execute($this);

            unset($ojbect);
        }
        else
        {
            // throw new Exception('Can not excecute requeset without controller.'. $this->_controller);
        }
        return $this;
    }

    /**
     * instance a new request
     * @return Request
     */
    static public function factory($controller, $params = null)
    {
        return new self($controller, $params);
    }

}
